#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void Usage(std::string proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

struct ThreadData
{
    int _sockfd;
    struct sockaddr_in _server;
};

void *sendmessage(void *args)
{
    // 3.向服务器发送消息
    struct ThreadData *sendData = static_cast<struct ThreadData *>(args);
    std::string message;
    socklen_t len = sizeof(sendData->_server);
    while (1)
    {
        message.clear();
        getline(std::cin, message);
        sendto(sendData->_sockfd, message.c_str(), message.size(), 0, (const struct sockaddr *)&sendData->_server, len);
    }
}

void *recvmessage(void *args)
{
    // 客户端收到服务器发来的消息
    struct ThreadData *recvData = static_cast<struct ThreadData *>(args);
    char buffer[1024];
    socklen_t len=sizeof(recvData->_server);
    while (1)
    {
        memset(buffer, 0, sizeof(buffer));
        socklen_t len = sizeof(recvData->_server);
        int n = recvfrom(recvData->_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&(recvData->_server), &len);
        if (n > 0)
        {
            std::cerr << buffer << std::endl;
            memset(buffer, 0, sizeof(buffer));
        }
    }
}

// 客户端:输入的是服务器的ip
int main(int argc, char *argv[])
{
    // 1.客户端从网络中获取数据，得创建套接字
    ThreadData sendData;
    ThreadData recvData;
    recvData._sockfd = sendData._sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sendData._sockfd < 0)
    {
        std::cout << "socker error" << std::endl;
        return 1;
    }

    if (argc != 3)
    {
        Usage(argv[0]);
        exit(0);
    }

    std::string ip = argv[1];
    std::uint16_t port = std::stoi(argv[2]);
    // 主机信息 :通过参数列表来获取
    // 还得转换为网络字节序列
    sendData._server.sin_family = AF_INET;
    sendData._server.sin_port = htons(port);
    sendData._server.sin_addr.s_addr = inet_addr(ip.c_str()); // 四点ip转化为4字节ip
    socklen_t len = sizeof(sendData._server);
    // 2.绑定，我们自己不要手动绑定，一般由OS自由随机选择！
    
    // 由于收到的服务器信息和发送服务器结构体内容是一样的

    // 加入线程版:加入两个线程，收发
    pthread_t tid1;
    pthread_t tid2;
    // 由于sockfd也得传入，因此得建立一个结构体
    pthread_create(&tid1, nullptr, sendmessage, (void *)&sendData);
    pthread_create(&tid1, nullptr, recvmessage, (void *)&recvData);

    pthread_join(tid1,nullptr);
    pthread_join(tid2,nullptr);

    close(sendData._sockfd);

    return 0;
}
